/*****************************************************************************
*    Copyright (C)2004 Ali Corporation. All Rights Reserved.
*
*    File:    nim_m3328f_ext.c
*
*    Description:    This file contains m3328f external QPSK/tuner features.
*    History:
*           Date            Athor        Version          Reason
*	    ============	=============	=========	=================
*	1.	2007-12-4	Eric Li 	V1.000		Initialize
*
*****************************************************************************/
#include <sys_config.h>
#include <retcode.h>
#include <types.h>
#include <osal/osal.h>
#include <api/libc/alloc.h>
#include <api/libc/printf.h>
#include <api/libc/string.h>
#include <hal/hal_gpio.h>
#include <hld/hld_dev.h>
#include <hld/nim/nim_dev.h>
#include <hld/nim/nim.h>
#include <hld/nim/nim_tuner.h>
#include <hld/sto/sto.h>
#include <hld/sto/sto_dev.h>
#include <api/libdiseqc/lib_diseqc.h>
#include <bus/erom/uart.h>
#include <bus/i2c/i2c.h>
#include "nim_m3327.h"

#define MAX_RETRY_NUM 1
OSAL_ID nim_28f_ext_erom_sem = OSAL_INVALID_ID;
OSAL_ID nim_28f_reset_flag = OSAL_INVALID_ID;
static UINT32 g_uart_speed_sel;
static UINT8 g_gpio_28f_reset;
static UINT8 erom_28f_synced = FALSE;
static UINT8 g_uart_id;

extern char nim_m3327_name[HLD_MAX_NAME_SIZE];

INT32 nim_28f_ext_erom_rm(UINT32 a, UINT8 *b, UINT32 c, UINT16 d)
{
	return nim_28f_ext_erom_rw(a,(void *)b,c,d,1);
}
INT32 nim_28f_ext_erom_wm(UINT32 a, UINT8 *b, UINT32 c, UINT16 d)
{
	return nim_28f_ext_erom_rw(a,(void *)b,c,d,0);
}
static INT32 nim_28f_ext_read(UINT8 bMemAdr, UINT8 *pData, UINT8 bLen)
{
	UINT32 addr = NIM_M3327_BASE_IO_ADR + bMemAdr;
	INT32 ret = SUCCESS;
	
	osal_semaphore_capture(nim_28f_ext_erom_sem,OSAL_WAIT_FOREVER_TIME);

	if(erom_28f_synced)
		nim_28f_ext_erom_rw(addr, pData, 1, 0, 1);
	else
		ret = ERR_FAILUE;

	osal_semaphore_release(nim_28f_ext_erom_sem);

	return ret;
}

static INT32 nim_28f_ext_write(UINT8 bMemAdr, UINT8 *pData, UINT8 bLen)
{
	UINT32 addr = NIM_M3327_BASE_IO_ADR + bMemAdr;
	INT32 ret = SUCCESS;
	
	osal_semaphore_capture(nim_28f_ext_erom_sem,OSAL_WAIT_FOREVER_TIME);

	if(erom_28f_synced)
		nim_28f_ext_erom_rw(addr, pData, 1, 0, 0);
	else
		ret = ERR_FAILUE;

	osal_semaphore_release(nim_28f_ext_erom_sem);

	return ret;
}
static BOOL sync_slave_28f_ext(UINT8 mode, UINT32 sync_tmo)
{
	INT32 result, user_int, retry_count = 0;

	NIM_PRINTF("Synchronizing..., type: 3\n");
	while(1)
	{
		HAL_GPIO_BIT_SET(g_gpio_28f_reset, HAL_GPIO_O_DIR);
		//sync_twinkle();
		result = nim_28f_ext_erom_sync(sync_tmo, mode, g_uart_id);
		
		if(SUCCESS == result)
			break;
		
		HAL_GPIO_BIT_SET(g_gpio_28f_reset, !HAL_GPIO_O_DIR);
		retry_count++;
		
		if(retry_count > MAX_RETRY_NUM)
		{
			NIM_PRINTF("Synchronization failed. type: 2\n");
			return FALSE;
		}
		if(retry_count)
		{
			NIM_PRINTF("Synchronization time out, retry %d, type: 2\n", retry_count);
		}
	}

	//    unsigned char data8 = 0x6;
	//    nim_28f_ext_erom_wm(0xB800009B,&data8,1,0);

	return TRUE;
}

static RET_CODE nim_28f_ext_hw_init(struct nim_device *dev)
{
	UINT8 data8;
	UINT16 chip_id;
	struct nim_m3327_private * priv = (struct nim_m3327_private*)dev->priv;

	HAL_GPIO_BIT_DIR_SET(g_gpio_28f_reset, HAL_GPIO_O_DIR);
	HAL_GPIO_BIT_SET(g_gpio_28f_reset, !HAL_GPIO_O_DIR);
	
	if(sync_slave_28f_ext(P2P_MODE, 1000))
	{
		NIM_PRINTF("'Erom sync successful! type: 2\n");
	}
	else
	{
		NIM_PRINTF("Erom sync failed! type: 2\n");
		return RET_FAILURE;
	}

	nim_28f_ext_uart_speed_config(g_uart_speed_sel);
	
	//i2c_mode_set(I2C_TYPE_SCB_RM, 40000, 1);
	if(0)//GPIO testing.
	{
		UINT32 reg = 0xffffffff;
		nim_28f_ext_erom_wm(0xb8000058, (UINT8 *)&reg, 4, 0);

		while(1)
		{
			osal_delay(65534);
			reg = 0;
			nim_28f_ext_erom_wm(0xb8000054, (UINT8 *)&reg, 4, 0);
			osal_delay(65534);
			reg = 0xffffffff;
			nim_28f_ext_erom_wm(0xb8000054, (UINT8 *)&reg, 4, 0);
		}
	}

	chip_id = 0;
	nim_28f_ext_erom_rm(0xb8000002, (UINT8 *)&data8, 1, 0);
	chip_id |= data8;
	nim_28f_ext_erom_rm(0xb8000003, (UINT8 *)&data8, 1, 0);
	chip_id |= (data8<<8);
	
	if(0x3327==chip_id) //M3327C/E/G serial chip.
	{
		nim_28f_ext_erom_rm(0xb8000000, (UINT8 *)&data8, 1, 0);
		if(data8>=0xf7) //S3327G or higher
			priv->is_m3327g = TRUE;
		else
			priv->is_m3327g = FALSE;
	}
	else
		return RET_FAILURE;
	
	// Set slave to QPSK only mode
	// 0xb8000028 <-- 0x000c0001
	data8 = 0x01;
	nim_28f_ext_erom_wm(0xb8000028, (UINT8 *)&data8, 1, 0);

	data8 = 0x00;
	nim_28f_ext_erom_wm(0xb8000029, (UINT8 *)&data8, 1, 0);

	data8 = 0x0c;
	nim_28f_ext_erom_wm(0xb800002a, (UINT8 *)&data8, 1, 0);

	data8 = 0x00;
	nim_28f_ext_erom_wm(0xb800002b, (UINT8 *)&data8, 1, 0);


	if(priv->is_m3327g)
	{
	#if (NIM_M3327G_SSI_CLOCK==54000)
		/*Config S3327G4 SSI clock to 54M*/
		data8 = 0x80;
		nim_28f_ext_erom_wm(0xb80000c1, (UINT8 *)&data8, 1, 0);
	#elif (NIM_M3327G_SSI_CLOCK==67500)
		/*Config S3327G4 SSI clock to 67.5M*/
		/*1. Config S3327G4 SSI clock to Half memory clock*/
		data8 = 0x40;
		nim_28f_ext_erom_wm(0xb80000c1, (UINT8 *)&data8, 1, 0);

		/*2. Config memory clock to 135MHz*/
		data8 = 0x0<<4;
		nim_28f_ext_erom_wm(0xb8000074, (UINT8 *)&data8, 1, 0);
		data8 = 0x2;
		nim_28f_ext_erom_wm(0xb8000077, (UINT8 *)&data8, 1, 0);
	#elif (NIM_M3327G_SSI_CLOCK==72000)
		/*Config S3327G4 SSI clock to 72M*/
		data8 = 0x00;
		nim_28f_ext_erom_wm(0xb80000c1, (UINT8 *)&data8, 1, 0);

	#endif
	}
	/*Config m3328f PAD drive, 0x0a~0x3a*/
	/*data8 = 0x1a;
	nim_28f_ext_erom_wm(0xb80000a0, (UINT8 *)&data8, 1, 0);*/

	/*<=======Verify the remote system register setting.=====*/
	#if 0
	nim_28f_ext_erom_rm(0xb8000028, (UINT8 *)&data8, 1, 0);
	NIM_PRINTF("0x%02x\n", data8);

	nim_28f_ext_erom_rm(0xb8000029, (UINT8 *)&data8, 1, 0);
	NIM_PRINTF("0x%02x\n", data8);

	nim_28f_ext_erom_rm(0xb800002a, (UINT8 *)&data8, 1, 0);
	NIM_PRINTF("0x%02x\n", data8);

	nim_28f_ext_erom_rm(0xb800002b, (UINT8 *)&data8, 1, 0);
	NIM_PRINTF("0x%02x\n", data8);

	/*m3328f PAD drive*/
	/*nim_28f_ext_erom_rm(0xb80000A0, (UINT8 *)&data8, 1, 0);
	NIM_PRINTF("s3328f spi PAD DRIVE:0x%02x\n", data8);//0X0A~0X3A*/

	if(priv->is_m3327g)
	{
		/*S3327G4 SSI clock configuration*/
		/*nim_28f_ext_erom_rm(0xb80000c1, (UINT8 *)&data8, 1, 0);
		NIM_PRINTF("S3327G4 SSI clock configuration:0x%02x\n", data8);*/
	}
	#endif
	/* =================End of verify============>*/

	
	// Stop slave clock to save power
	// 0xb8000060 <-- 0x05040000
	data8 = 0x04;
	nim_28f_ext_erom_wm(0xb8000060 + 2, (UINT8 *)&data8, 1, 0);

	data8 = 0x05;
	nim_28f_ext_erom_wm(0xb8000060 + 3, (UINT8 *)&data8, 1, 0);

	return RET_SUCCESS;
}

/*****************************************************************************
* INT32 nim_28f_ext_open(struct nim_device *dev)
* Description: M3328F external NIM open
*
* Arguments:
*  Parameter1: struct nim_device *dev
*
* Return Value: INT32
*****************************************************************************/
__ATTRIBUTE_REUSE_
static INT32 nim_28f_ext_open(struct nim_device *dev)
{
	UINT8 data;
	struct nim_m3327_private * priv = (struct nim_m3327_private*)dev->priv;

	NIM_PRINTF("	Enter fuction nim_m3327_open\n");

	if(FALSE==erom_28f_synced)
	{
		if(RET_SUCCESS!=nim_28f_ext_hw_init(dev))
			return RET_FAILURE;
	}

	erom_28f_synced = TRUE;
	nim_m3327_after_reset_set_param(dev);

	//CR01
	data = 0x0A | ((priv->Tuner_Config_Data.Connection_config&0x07)<<4);
	priv->nim_reg_write(0x01, &data, 1);

	NIM_PRINTF("	Leave fuction nim_m3327_open\n");
	return RET_SUCCESS;
}

/*****************************************************************************
* INT32 nim_28f_ext_close(struct nim_device *dev)
* Description: M3328F external NIM close
*
* Arguments:
*  Parameter1: struct nim_device *dev
*
* Return Value: INT32
*****************************************************************************/
static INT32 nim_28f_ext_close(struct nim_device *dev)
{
	struct nim_m3327_private * priv = (struct nim_m3327_private*)dev->priv;
	UINT8  data;

	//CR00
	data = 0x90;
	priv->nim_reg_write(0x00, &data, 1);

	//CR7F
	data = 0xBF;
	priv->nim_reg_write(0x7F, &data, 1);

	//osal_semaphore_delete(nim_28f_ext_erom_sem);
	HAL_GPIO_BIT_SET(g_gpio_28f_reset, !HAL_GPIO_O_DIR);
	erom_28f_synced = FALSE;
	priv->ext_nim_valid = FALSE;
	return SUCCESS;
}

void nim_28f_ext_reset_monitor(UINT32 param1, UINT32 para2)
{
	UINT32 flgptn;
	OSAL_ER er;
	struct nim_device *nim_dev = (struct nim_device *)param1;
	struct nim_m3327_private * priv = (struct nim_m3327_private*)nim_dev->priv;
	UINT8 test_read;
	while(1)
	{
		er = osal_flag_wait(&flgptn, nim_28f_reset_flag, NIM_EXT_DEMOD_RESET, OSAL_TWF_ORW, OSAL_WAIT_FOREVER_TIME);
		if((OSAL_E_OK==er) && (flgptn&NIM_EXT_DEMOD_RESET))
		{
			NIM_PRINTF("Nim m3328f external demode reset.\n");
			HAL_GPIO_BIT_SET(g_gpio_28f_reset, !HAL_GPIO_O_DIR);
			priv->ext_nim_valid = FALSE;
			erom_28f_synced = FALSE;
			osal_delay(100);
			nim_28f_ext_open(nim_dev);
			if(erom_28f_synced)
			{
				osal_flag_clear(nim_28f_reset_flag, NIM_EXT_DEMOD_RESET);
				priv->ext_nim_valid = TRUE;
			}
		}
		osal_task_sleep(500);
/*
		//Verify external NIM is recovered.
		nim_28f_ext_read(0x00, &test_read, 1);
		if(osal_flag_wait(&flgptn, nim_28f_reset_flag, NIM_EXT_DEMOD_RESET, OSAL_TWF_ORW, 0) == OSAL_E_OK)
		{
			NIM_PRINTF("Nim m3328f external demode reset failed.\n");
			continue;
		}
		NIM_PRINTF("Nim m3328f external demode reset complete.\n");
*/
	}
}

/*****************************************************************************
* INT32 nim_28f_ext_attach (struct QPSK_TUNER_CONFIG_API * ptrQPSK_Tuner, struct EXT_QPSK_Config *ext_28f_param)
* Description: M3328F external NIM initialization
*
* Arguments:
*  none
*
* Return Value: INT32
*****************************************************************************/
__ATTRIBUTE_REUSE_
INT32 nim_28f_ext_attach (struct QPSK_TUNER_CONFIG_API * ptrQPSK_Tuner, struct EXT_QPSK_Config *ext_28f_param)
{
	struct nim_device *dev;
	struct nim_m3327_private *priv_mem;
	T_CTSK nim_task_param ;
	UINT32 chip_id;
	UINT32 chip_rev;
	UINT32 uart_strap_pin_bit;
	chip_id  = sys_ic_get_chip_id();
	chip_rev = sys_ic_get_rev_id(); 

	// Check whether Tuner API is avalidate
	if (ptrQPSK_Tuner == NULL)
	{
		NIM_PRINTF("Tuner Configuration API structure is NULL!/n");
		return ERR_NO_DEV;
	}
	else if ( (ptrQPSK_Tuner->nim_Tuner_Control == NULL) || (ptrQPSK_Tuner->nim_Tuner_Status == NULL) )
	{
		// check callback API Function is available or not
		NIM_PRINTF("Tuner Configuration API structure is NULL!/n");
		return ERR_NO_DEV;
	}

	nim_m3327_name[STRLEN(nim_m3327_name)-1] += 1;
	dev = (struct nim_device *)dev_alloc(nim_m3327_name, HLD_DEV_TYPE_NIM, sizeof(struct nim_device));
	if (dev == NULL)
	{
		NIM_PRINTF("Error: Alloc nim device error!\n");
		return ERR_NO_MEM;
	}

	/* Alloc structure space of private */
	priv_mem = (struct nim_m3327_private *)MALLOC(sizeof(struct nim_m3327_private));
	if (priv_mem == NULL)
	{
		dev_free(dev);
		NIM_PRINTF("Alloc nim device prive memory error!/n");
		return ERR_NO_MEM;
	}
	MEMSET(priv_mem, 0, sizeof(struct nim_m3327_private));
	dev->priv = (void *)priv_mem;

	/* tuner configuration function */
	priv_mem->nim_Tuner_Init = ptrQPSK_Tuner->nim_Tuner_Init;
	priv_mem->nim_Tuner_Control = ptrQPSK_Tuner->nim_Tuner_Control;
	priv_mem->nim_Tuner_Status = ptrQPSK_Tuner->nim_Tuner_Status;
	priv_mem->Tuner_Config_Data.Recv_Freq_Low = ptrQPSK_Tuner->config_data.Recv_Freq_Low;
	priv_mem->Tuner_Config_Data.Recv_Freq_High = ptrQPSK_Tuner->config_data.Recv_Freq_High;
	priv_mem->Tuner_Config_Data.Ana_Filter_BW = ptrQPSK_Tuner->config_data.Ana_Filter_BW;
	priv_mem->Tuner_Config_Data.Connection_config = ptrQPSK_Tuner->config_data.Connection_config;
	priv_mem->Tuner_Config_Data.AGC_Threshold_1 = ptrQPSK_Tuner->config_data.AGC_Threshold_1;
	priv_mem->Tuner_Config_Data.AGC_Threshold_2 = ptrQPSK_Tuner->config_data.AGC_Threshold_2;
	priv_mem->Tuner_Config_Data.QPSK_Config = ptrQPSK_Tuner->config_data.QPSK_Config;
	priv_mem->nim_reg_read = nim_28f_ext_read;
	priv_mem->nim_reg_write = nim_28f_ext_write;
	priv_mem->is_ext_nim = TRUE;
	priv_mem->ext_qpsk_mode = ext_28f_param->ext_qpsk_mod;

	g_gpio_28f_reset = ext_28f_param->gpio_28f_reset;
	g_uart_id = ext_28f_param->host_uart_id;

	//update uart base address and uart reset bits array by chip ID 
	uart_base_addr_update(chip_id);
	
	if((ALI_M3329E == chip_id) && (chip_rev >= IC_REV_5))
	{
		uart_strap_pin_bit = 0x00001000;
	}
	else if(ALI_S3602 == chip_id)
	{
		uart_strap_pin_bit = 0x00008000;
	}
	else
	{
		uart_strap_pin_bit = 0x00000400;
	}
	if(0==((*(volatile UINT32*)(0xb8000070))&uart_strap_pin_bit))
		g_uart_speed_sel = UART_HIGH_SPEED_2M;
	else
		g_uart_speed_sel = UART_HIGH_SPEED_6M;
	
//diseqc state init
	dev->diseqc_info.diseqc_type = 0;
	dev->diseqc_info.diseqc_port = 0;
	dev->diseqc_info.diseqc_k22 = 0;

	if ((priv_mem->Tuner_Config_Data.QPSK_Config & 0x10) == 0x10) //bit4: polarity revert.
		dev->diseqc_info.diseqc_polar = LNB_POL_V;
	else //default usage, not revert.
		dev->diseqc_info.diseqc_polar = LNB_POL_H;

	dev->diseqc_typex = 0;
	dev->diseqc_portx = 0;

	/* Function point init */
	dev->base_addr = SYS_QPSK_CHIP_ADRRESS;
	dev->init = nim_28f_ext_attach;
	dev->open = nim_28f_ext_open;
	dev->stop = nim_28f_ext_close;
	dev->do_ioctl = nim_m3327_ioctl;
	dev->set_polar = nim_m3327_set_polar;
	dev->set_12v = nim_m3327_set_12v;
//	dev->channel_change = nim_m3327_channel_change;
	dev->channel_search = nim_m3327_channel_search;
	dev->DiSEqC_operate = nim_m3327_DiSEqC_operate;
	dev->DiSEqC2X_operate = nim_m3327_DiSEqC2X_operate;
	dev->get_lock = nim_m3327_get_lock;
	dev->get_freq = nim_m3327_get_freq;
	dev->get_sym = nim_m3327_get_symbol_rate;
	dev->get_FEC = nim_m3327_get_code_rate;
	dev->get_AGC = nim_m3327_get_AGC;
	dev->get_SNR = nim_m3327_get_SNR;
	dev->get_BER = nim_m3327_get_BER;
	/*fft function require - by Joey*/
	dev->get_fft_result = nim_m3327_get_fft_result;
	dev->get_ver_infor = nim_m3327_get_ver_infor;

	/* I/O Control Extension Function -- zhujun 2005-11-21 */
	dev->do_ioctl_ext = nim_m3327_ioctl_ext;



	/* Add this device to queue */
	if (dev_register(dev) != SUCCESS)
	{
		NIM_PRINTF("Error: Register nim device error!\n");
		FREE(priv_mem);
		dev_free(dev);
		return ERR_NO_DEV;
	}

	// initialize the pointer to DEV Private MEM: m3327_dev_priv
	//m3327_dev_priv = (struct nim_m3327_private*)dev->priv;

	// Initial the QPSK Tuner
	if (priv_mem->nim_Tuner_Init != NULL)
	{
		NIM_PRINTF(" %s: Initial the Tuner \n", __FUNCTION__);

		if (priv_mem->nim_Tuner_Init (&(priv_mem->tuner_id),&(ptrQPSK_Tuner->tuner_config)) != SUCCESS)
		{
			NIM_PRINTF("Error: Init Tuner Failure!\n");
			FREE(priv_mem);
			dev_free(dev);
			return ERR_NO_DEV;
		}
	}

	if(RET_SUCCESS!=nim_28f_ext_hw_init(dev))
	{
		NIM_PRINTF("Error: external demode HW initialization failed!\n");
		FREE(priv_mem);
		dev_free(dev);
		return ERR_NO_DEV;
	}
	if(EXT_QPSK_MODE_SSI==priv_mem->ext_qpsk_mode)
	{
		if((priv_mem->is_m3327g) || ((ALI_M3329E == chip_id) && (chip_rev >= IC_REV_5))||(ALI_S3602 == chip_id))
		{
			priv_mem->tsi_mode = 0x8b;
			ext_28f_param->tsi_mode = 0x8b;
		}
		else
		{
			priv_mem->tsi_mode = 0x9b;
			ext_28f_param->tsi_mode = 0x9b;
		}
	}
	else
	{
		priv_mem->tsi_mode = 0x83;
		ext_28f_param->tsi_mode = 0x83;
	}

	nim_28f_ext_erom_sem = osal_semaphore_create(1);
	if(INVALID_ID==nim_28f_ext_erom_sem)
	{
		NIM_PRINTF("Error: Create EROM semaphore failed!\n");
		FREE(priv_mem);
		dev_free(dev);
		return ERR_NO_DEV;
	}

	nim_28f_reset_flag= osal_flag_create(0x00000000);
	if(INVALID_ID==nim_28f_reset_flag)
	{
		NIM_PRINTF("Error: Create reset monitor flag failed!\n");
		FREE(priv_mem);
		dev_free(dev);
		osal_semaphore_delete(nim_28f_ext_erom_sem);
		nim_28f_ext_erom_sem = OSAL_INVALID_ID;
		return ERR_NO_DEV;
	}

	nim_task_param.task=nim_28f_ext_reset_monitor;
	nim_task_param.itskpri = OSAL_PRI_HIGH;
	nim_task_param.quantum = 10;
	nim_task_param.stksz = 0x400;
	nim_task_param.para1 = (UINT32)dev;
	nim_task_param.name[0] = 'N';
	nim_task_param.name[1] = 'R';
	nim_task_param.name[2] = 'M';
	if(INVALID_ID==osal_task_create(&nim_task_param))
	{
		NIM_PRINTF("Error: Create external demode reset monitor task failed!\n");
		FREE(priv_mem);
		dev_free(dev);
		osal_semaphore_delete(nim_28f_ext_erom_sem);
		osal_flag_delete(nim_28f_reset_flag);
		nim_28f_ext_erom_sem = OSAL_INVALID_ID;
		nim_28f_reset_flag = OSAL_INVALID_ID;
		return ERR_NO_DEV;
	}

	/****For external lnb controller config****/
	priv_mem->ext_lnb_control = NULL;
	if(ptrQPSK_Tuner->ext_lnb_config.ext_lnb_control)
	{
		UINT32 check_sum = 0;
		check_sum = (UINT32)(ptrQPSK_Tuner->ext_lnb_config.ext_lnb_control);
		check_sum += ptrQPSK_Tuner->ext_lnb_config.i2c_base_addr;
		check_sum += ptrQPSK_Tuner->ext_lnb_config.i2c_type_id;
		if(check_sum==ptrQPSK_Tuner->ext_lnb_config.param_check_sum)
		{
			priv_mem->ext_lnb_control = ptrQPSK_Tuner->ext_lnb_config.ext_lnb_control;
			priv_mem->ext_lnb_control(0, LNB_CMD_ALLOC_ID, (UINT32)(&priv_mem->ext_lnb_id));
			priv_mem->ext_lnb_control(priv_mem->ext_lnb_id, LNB_CMD_INIT_CHIP, (UINT32)(&ptrQPSK_Tuner->ext_lnb_config));
		}
	}

	erom_28f_synced = TRUE;
	priv_mem->ext_nim_valid = TRUE;
	return SUCCESS;
}

/*
void nim_28f_ext_dum_reg(void)
{
	UINT32 i;
	libc_printf("\ndump QPSK reg: \n");
	for(i = 0; i<0x8f; i++)
	{
		UINT8 val = 0;
		nim_28f_ext_erom_rw(0xb8003000+i, &val, 1, 0, 1);
		libc_printf("%02x: %02x\n", i, val);
	}

	libc_printf("\ndump sys reg: \n");
	for(i = 0; i<0xa0; i++)
	{
		UINT8 val = 0;
		nim_28f_ext_erom_rw(0xb8000000 + i, &val, 1, 0, 1);
		libc_printf("%02x: %02x\n", i, val);
	}
}
*/
#ifdef NIM_M3327G_SFU_TEST
#define GET_DWORD(i)           (*(volatile UINT32 *)(i))
#define SET_DWORD(i,d)        (*(volatile UINT32 *)(i)) = (d)

#define GET_WORD(i)             (*(volatile UINT16 *)(i))
#define SET_WORD(i,d)          (*(volatile UINT16 *)(i)) = (d)

void nim_m3327g_ssi_set_clk(struct nim_device *dev, UINT8 clk)
{
	struct nim_m3327_private * priv = (struct nim_m3327_private*)dev->priv;
	UINT8 data8;
	UINT32 dmx_tsi_spi = 0;
	UINT16 dmx_tsi_ssi = 0;
	if((!priv->is_ext_nim) || (!priv->is_m3327g)|| (EXT_QPSK_MODE_SSI != priv->ext_qpsk_mode))
		return;

	dmx_tsi_spi = GET_DWORD(0xb8009000)&0x80808080;
	dmx_tsi_ssi = GET_WORD(0xb8009004)&0x8080;
	SET_DWORD(0xb8009000, GET_DWORD(0xb8009000)&0x7f7f7f7f);
	SET_WORD(0xb8009004, GET_WORD(0xb8009004)&0x7f7f);
	
	switch(clk) //in MHz
	{
	case 54:
		/*Config S3327G4 SSI clock to 54M*/
		data8 = 0x80;
		nim_28f_ext_erom_wm(0xb80000c1, (UINT8 *)&data8, 1, 0);
		break;
	case 72:
		/*Config S3327G4 SSI clock to 72M*/
		data8 = 0x00;
		nim_28f_ext_erom_wm(0xb80000c1, (UINT8 *)&data8, 1, 0);
		break;
	case 67:
	case 68:
		/*Config S3327G4 SSI clock to Half memory clock*/
		data8 = 0x40;
		nim_28f_ext_erom_wm(0xb80000c1, (UINT8 *)&data8, 1, 0);

		/*Config memory clock to 135MHz*/
		data8 = 0x0<<4;
		nim_28f_ext_erom_wm(0xb8000074, (UINT8 *)&data8, 1, 0);
		data8 = 0x2;
		nim_28f_ext_erom_wm(0xb8000077, (UINT8 *)&data8, 1, 0);
		break;
	case 60:
		/*Config S3327G4 SSI clock to Half memory clock*/
		data8 = 0x40;
		nim_28f_ext_erom_wm(0xb80000c1, (UINT8 *)&data8, 1, 0);

		/*Config memory clock to 120MHz*/
		data8 = 0x1<<4;
		nim_28f_ext_erom_wm(0xb8000074, (UINT8 *)&data8, 1, 0);
		data8 = 0x2;
		nim_28f_ext_erom_wm(0xb8000077, (UINT8 *)&data8, 1, 0);
		break;
	case 49:
		/*Config S3327G4 SSI clock to Half memory clock*/
		data8 = 0x40;
		nim_28f_ext_erom_wm(0xb80000c1, (UINT8 *)&data8, 1, 0);

		/*Config memory clock to 98MHz*/
		data8 = 0x2<<4;
		nim_28f_ext_erom_wm(0xb8000074, (UINT8 *)&data8, 1, 0);
		data8 = 0x2;
		nim_28f_ext_erom_wm(0xb8000077, (UINT8 *)&data8, 1, 0);
		break;
	default:
		break;
	}

	SET_DWORD(0xb8009000, GET_DWORD(0xb8009000)|dmx_tsi_spi);
	SET_WORD(0xb8009004, GET_WORD(0xb8009004)|dmx_tsi_ssi);
}

void nim_m3327g_set_gap(struct nim_device *dev, UINT8 gap)
{
	struct nim_m3327_private * priv = (struct nim_m3327_private*)dev->priv;
	UINT8 data;
	UINT32 dmx_tsi_spi = 0;
	UINT16 dmx_tsi_ssi = 0;
	if((!priv->is_ext_nim) || (!priv->is_m3327g))
		return;

	dmx_tsi_spi = GET_DWORD(0xb8009000)&0x80808080;
	dmx_tsi_ssi = GET_WORD(0xb8009004)&0x8080;
	SET_DWORD(0xb8009000, GET_DWORD(0xb8009000)&0x7f7f7f7f);
	SET_WORD(0xb8009004, GET_WORD(0xb8009004)&0x7f7f);

	switch(gap)
	{
	case 0:
		if(EXT_QPSK_MODE_SSI == priv->ext_qpsk_mode)
			data = 0x08;
		else
			data = 0x01;
		priv->nim_reg_write(0x94, &data, 1);
		break;
	case 4:
		if(EXT_QPSK_MODE_SSI == priv->ext_qpsk_mode)
			data = 0x0a;
		else
			data = 0x03;
		priv->nim_reg_write(0x94, &data, 1);
		break;
	case 8:
		if(EXT_QPSK_MODE_SSI == priv->ext_qpsk_mode)
			data = 0x0c;
		else
			data = 0x05;
		priv->nim_reg_write(0x94, &data, 1);
		break;
	case 16:
		if(EXT_QPSK_MODE_SSI == priv->ext_qpsk_mode)
			data = 0x0e;
		else
			data = 0x07;
		priv->nim_reg_write(0x94, &data, 1);
		break;
	default:
		break;
	}

	SET_DWORD(0xb8009000, GET_DWORD(0xb8009000)|dmx_tsi_spi);
	SET_WORD(0xb8009004, GET_WORD(0xb8009004)|dmx_tsi_ssi);
}

#endif

